﻿using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using StarsAdmin.Application.Auth.Dtos;
using StarsAdmin.Application.Menu.Dtos;
using StarsAdmin.Core.App;
using StarsAdmin.Core.Cache;
using StarsAdmin.Core.Consts;
using StarsAdmin.Core.DB.Entities;
using StarsAdmin.Core.Enums;
using StarsAdmin.Core.Exceptions;
using StarsAdmin.Core.Extensions;
using StarsAdmin.Core.Helpers;
using StarsAdmin.Core.Models;
using StarsAdmin.Repository.Menu;
using StarsAdmin.Repository.Role;
using StarsAdmin.Repository.User;
using System.Diagnostics;

namespace StarsAdmin.Application.Auth.Services
{
    public class AuthService : IAuthService
    {
        private readonly IMapper _mapper;
        private readonly ICacheService _cache;
        private readonly IUserRepository _userRepository;
        private readonly IRoleRepository _roleRepository;
        private readonly IMenuRepository _menuRepository;
        private readonly IHttpContextAccessor _httpContextAccessor;

        public AuthService(IMapper mapper, ICacheService cacheService, IHttpContextAccessor httpContextAccessor, IUserRepository userRepository, IRoleRepository roleRepository, IMenuRepository menuRepository)
        {
            _mapper = mapper;
            _cache = cacheService;
            _userRepository = userRepository;
            _httpContextAccessor = httpContextAccessor;
            _roleRepository = roleRepository;
            _menuRepository = menuRepository;
        }

        public async Task<CurrentUserDto?> LoginAsync(LoginInput loginInput)
        {
            var admin = await _userRepository.GetBy(e => e.UserName == loginInput.UserName).Include(e => e.Roles).AsNoTracking().FirstOrDefaultAsync();
            if (admin is null)
            {
                throw new NotFoundException("账号错误");
            }
            // 验证密码
            var pwHash = MD5Helper.MD5Encrypt32(admin.PasswordSalt + loginInput.Password);
            if (pwHash != admin.PasswordHash)
            {
                throw new NotFoundException("密码错误");
            }
            var currentUser = _mapper.Map<CurrentUserDto>(admin);
            //获得权限字段
            currentUser.AuthFields = await GetAuthFieldsAsync(currentUser.Id);
            // 随机生成32位Token版本号
            var version = StringHelper.GenerateRandomString();
            // 生成token
            var token = JwtHelper.GenerateAccessToken(currentUser.Id, currentUser.UserName, version, currentUser.RoleIdentifies);
            // 将版本号添加到本地缓存
            await _cache.SetAsync(currentUser.Id, CacheKeyConst.TokenVersionKey, version, AppConfigs.Jwt.Expire);
            // 生成刷新token
            var refreshToken = JwtHelper.GenerateRefreshToken();

            currentUser.AccessToken = token;
            currentUser.RefreshToken = refreshToken;

            // 将刷新Token存储到缓存中，并设置过期时间
            await _cache.SetAsync(currentUser.Id, CacheKeyConst.RefreshTokenKey, refreshToken, AppConfigs.Jwt.RefreshExpire);

            // 修改最后登录时间
            admin.LastLoginDateTime = DateTime.Now;
            var result = await _userRepository.AttachAndSaveAsync(admin, e => e.LastLoginDateTime);
            currentUser.LastLoginDateTime = admin.LastLoginDateTime;

            return currentUser;
        }

        public async Task<string> GetRefreshTokenAsync(string refreshToken)
        {
            var currentUser = await GetUserInfoAsync();
            if (currentUser == null)
            {
                throw new NotFoundException("Token无效");
            }
            // 检查刷新Token是否存在于缓存中
            if (!await _cache.ValidateAsync(currentUser.Id, CacheKeyConst.RefreshTokenKey, refreshToken))
            {
                throw new NotFoundException("刷新Token无效");
            }
            // 随机生成32位Token版本号
            var version = StringHelper.GenerateRandomString();
            // 生成token
            var token = JwtHelper.GenerateAccessToken(currentUser.Id, currentUser.UserName, version, currentUser.RoleIdentifies);
            // 将版本号添加到本地缓存
            await _cache.SetAsync(currentUser.Id, CacheKeyConst.TokenVersionKey, version, AppConfigs.Jwt.Expire);
            return token;
        }

        public async Task<CurrentUserDto?> GetUserInfoAsync()
        {
            // 获取token
            var accessToken = JwtHelper.GetToken(_httpContextAccessor.HttpContext);
            if (accessToken == null)
            {
                throw new NotFoundException("Token无效");
            }
            // 获得token中的用户信息
            var userId = JwtHelper.GetUserId(accessToken);
            // 获取当前用户
            var user = await _userRepository.GetBy(e => e.Id == userId).Include(e => e.Roles).AsNoTracking().FirstOrDefaultAsync();
            if (user is null)
            {
                throw new NotFoundException("当前用户不存在");
            }
            var currentUser = _mapper.Map<CurrentUserDto>(user);
            //获得权限字段
            currentUser.AuthFields = await GetAuthFieldsAsync(currentUser.Id);
            return currentUser;
        }

        public async Task<List<MenuDto>> GetAuthMenusAsync(long id)
        {
            return await _cache.GetOrSetAsync(CacheKeyConst.MenuCacheKey, id, async () =>
            {
                var result = await _userRepository.GetAll()
                                                  .AsNoTracking()
                                                  .Where(e => e.IsEnabled && e.Id == id)
                                                  .SelectMany(e => e.Roles)
                                                  .Where(e => e.IsEnabled)
                                                  .SelectMany(e => e.Menus)
                                                  .Where(e => e.Type != MenuTypeEnum.Button && e.IsEnabled)
                                                  .Include(e => e.Parent)
                                                  .Distinct()
                                                  .ToTreeAsync();
                return _mapper.Map<List<MenuDto>>(result);
            }, TimeConst.FailureTimeMinute);
        }

        public async Task<List<MenuDto>> GetAuthButtonsAsync(long id)
        {
            var menus = await _userRepository.GetAll()
                .Where(e => e.Id == id)
                .SelectMany(e => e.Roles)
                .SelectMany(e => e.Menus)
                .Where(e => e.Type == MenuTypeEnum.Button)
                .Include(e => e.Apis)
                .Distinct().AsNoTracking()
                .ToListAsync();
            return _mapper.Map<List<MenuDto>>(menus);
        }

        public async Task<List<string>> GetAuthButtonPermissionsAsync(long id)
        {
            return await _userRepository.GetAll()
                 .Where(e => e.Id == id)
                 .SelectMany(e => e.Roles)
                 .SelectMany(e => e.Menus)
                 .Where(e => e.Type == MenuTypeEnum.Button)
                 .Select(e => e.Permission)
                 .Distinct().AsNoTracking()
                 .ToListAsync();
        }

        private async Task<string[]> GetAuthFieldsAsync(long id)
        {
            return await _userRepository.GetAll()
                .Where(e => e.Id == id)
                .SelectMany(e => e.Roles)
                .SelectMany(e => e.Menus)
                .SelectMany(e => e.Apis)
                .Select(e => e.Path)
                .Distinct().AsNoTracking()
                .ToArrayAsync();
        }
    }
}